﻿// testUE.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#pragma comment(lib,"Core.lib")
#pragma comment(lib,"UEModel.lib")
#include "./Core.h"
#include "./UnCore.h"
#include "./UnObject.h"
#include "./UnrealPackage/UnPackage.h"
#include "./UnrealMesh/UnAnimNotify.h"

#include "./UnrealMaterial/UnMaterial.h"
#include "./UnrealMaterial/UnMaterial2.h"
#include "./UnrealMaterial/UnMaterial3.h"
#include "./UnrealMaterial/UnMaterialExpression.h"

#include "./UnrealMesh/UnMesh2.h"
#include "./UnrealMesh/UnMesh3.h"
#include "./UnrealMesh/UnMesh4.h"

#include "./UnSound.h"
#include "./UnThirdParty.h"

#include "../Exporters/Exporters.h"

#if DECLARE_VIEWER_PROPS
#include "./Mesh/SkeletalMesh.h"
#include "./Mesh/StaticMesh.h"
#endif

#include "./GameDatabase.h"
#include "./UnrealPackage/PackageUtils.h"



class UIProgressDialog : public IProgressCallback
{
    virtual bool Progress(const char* package, int index, int total) {
        return true;
    }
    virtual bool Tick() {
        return true;
    }

};

static void RegisterCommonUnrealClasses()
{
	// classes and structures
	RegisterCoreClasses();
	BEGIN_CLASS_TABLE

		REGISTER_MATERIAL_CLASSES
		REGISTER_ANIM_NOTIFY_CLASSES
#if BIOSHOCK
		REGISTER_MATERIAL_CLASSES_BIO
		REGISTER_MESH_CLASSES_BIO
#endif
#if SPLINTER_CELL
		REGISTER_MATERIAL_CLASSES_SCELL
#endif
#if UNREAL3
		REGISTER_MATERIAL_CLASSES_U3		//!! needed for Bioshock 2 too
#endif

#if DECLARE_VIEWER_PROPS
		REGISTER_SKELMESH_VCLASSES
		REGISTER_STATICMESH_VCLASSES
		REGISTER_MATERIAL_VCLASSES
#endif // DECLARE_VIEWER_PROPS

		END_CLASS_TABLE
		// enumerations
		REGISTER_MATERIAL_ENUMS
#if UNREAL3
		REGISTER_MATERIAL_ENUMS_U3
		REGISTER_MESH_ENUMS_U3
#endif
}


static void RegisterUnrealClasses2()
{
	BEGIN_CLASS_TABLE
		REGISTER_MESH_CLASSES_U2
#if UNREAL1
		REGISTER_MESH_CLASSES_U1
#endif
#if RUNE
		REGISTER_MESH_CLASSES_RUNE
#endif
		END_CLASS_TABLE
}


static void RegisterUnrealClasses3()
{
#if UNREAL3
	BEGIN_CLASS_TABLE
		//	REGISTER_MATERIAL_CLASSES_U3 -- registered for Bioshock in RegisterCommonUnrealClasses()
		REGISTER_MESH_CLASSES_U3
		REGISTER_EXPRESSION_CLASSES
#if TUROK
		REGISTER_MESH_CLASSES_TUROK
#endif
#if MASSEFF
		REGISTER_MESH_CLASSES_MASSEFF
#endif
#if DCU_ONLINE
		REGISTER_MATERIAL_CLASSES_DCUO
#endif
#if TRANSFORMERS
		REGISTER_MESH_CLASSES_TRANS
#endif
#if MKVSDC
		REGISTER_MESH_CLASSES_MK
#endif
		END_CLASS_TABLE
#endif // UNREAL3
		SuppressUnknownClass("UBodySetup");
	SuppressUnknownClass("UMaterialExpression*"); // wildcard
}


static void RegisterUnrealClasses4()
{
#if UNREAL4
	BEGIN_CLASS_TABLE
		REGISTER_MESH_CLASSES_U4
		REGISTER_MATERIAL_CLASSES_U4
		REGISTER_EXPRESSION_CLASSES
		END_CLASS_TABLE
		REGISTER_MATERIAL_ENUMS_U4
		REGISTER_MESH_ENUMS_U4
#endif // UNREAL4
		SuppressUnknownClass("UMaterialExpression*"); // wildcard
	SuppressUnknownClass("UMaterialFunction");
	SuppressUnknownClass("UPhysicalMaterial");
	SuppressUnknownClass("UBodySetup");
	SuppressUnknownClass("UNavCollision");
	SuppressUnknownClass("USkeletalMeshSocket");
}



static void RegisterUnrealSoundClasses()
{
	BEGIN_CLASS_TABLE
		REGISTER_SOUND_CLASSES
#if UNREAL3
		REGISTER_SOUND_CLASSES_UE3
#endif
#if TRANSFORMERS
		REGISTER_SOUND_CLASSES_TRANS
#endif
#if UNREAL4
		REGISTER_SOUND_CLASSES_UE4
#endif
		END_CLASS_TABLE
}


static void RegisterUnreal3rdPartyClasses()
{
#if UNREAL3
	BEGIN_CLASS_TABLE
		REGISTER_3RDP_CLASSES
		END_CLASS_TABLE
#endif
}

static void RegisterClasses(int game)
{
	// prepare classes
	// note: we are registering classes after loading package: in this case we can know engine version (1/2/3)
	RegisterCommonUnrealClasses();
	if (game < GAME_UE3)
	{
		RegisterUnrealClasses2();
	}
	else if (game < GAME_UE4_BASE)
	{
		RegisterUnrealClasses3();
		RegisterUnreal3rdPartyClasses();
	}
	else
	{
		RegisterUnrealClasses4();
	}
	/*if (GSettings.Startup.UseSound) */RegisterUnrealSoundClasses();

	// remove some class loaders when requested by command line
	//if (!GSettings.Startup.UseAnimation)
	//{
	//	UnregisterClass("MeshAnimation", true);
	//	UnregisterClass("AnimSet", true);
	//	UnregisterClass("AnimSequence", true);
	//	UnregisterClass("AnimNotify", true);
	//}
	//if (!GSettings.Startup.UseSkeletalMesh)
	//{
	//	UnregisterClass("SkeletalMesh", true);
	//	UnregisterClass("SkeletalMeshSocket", true);
	//	UnregisterClass("MorphTarget", false);
	//	if (!GSettings.Startup.UseAnimation)
	//		UnregisterClass("Skeleton", false);
	//}
	/*if (!GSettings.Startup.UseStaticMesh) UnregisterClass("StaticMesh", true);*/
	/*if (!GSettings.Startup.UseVertMesh) UnregisterClass("VertMesh", true);*/
	//if (!GSettings.Startup.UseTexture)
	//{
	//	UnregisterClass("UnrealMaterial", true);
	//	UnregisterClass("MaterialExpression", true);
	//	SuppressUnknownClass("UMaterial*");
	//}
	//if (!GSettings.Startup.UseMorphTarget) UnregisterClass("MorphTarget", false);
	//if (!GSettings.Startup.UseLightmapTexture) UnregisterClass("LightMapTexture2D", true);
	//if (!GSettings.Startup.UseScaleForm) UnregisterClass("SwfMovie", true);
	//if (!GSettings.Startup.UseFaceFx)
	//{
	//	UnregisterClass("FaceFXAnimSet", true);
	//	UnregisterClass("FaceFXAsset", true);
	//}
}


/*-----------------------------------------------------------------------------
	Exporters
-----------------------------------------------------------------------------*/


static void CallExportSkeletalMesh(const CSkeletalMesh* Mesh)
{
	assert(Mesh);
	ExportSkeletalMeshGLTF(Mesh);
	//switch (GSettings.Export.SkeletalMeshFormat)
	//{
	//case EExportMeshFormat::psk:
	//default:
	//	ExportPsk(Mesh);
	//	break;
	//case EExportMeshFormat::gltf:
	//	ExportSkeletalMeshGLTF(Mesh);
	//	break;
	//case EExportMeshFormat::md5:
	//	ExportMd5Mesh(Mesh);
	//	break;
	//}
}

static void CallExportStaticMesh(const CStaticMesh* Mesh)
{
	assert(Mesh);
	ExportStaticMeshGLTF(Mesh);
	//switch (GSettings.Export.StaticMeshFormat)
	//{
	//case EExportMeshFormat::psk:
	//default:
	//	ExportStaticMesh(Mesh);
	//	break;
	//case EExportMeshFormat::gltf:
	//	ExportStaticMeshGLTF(Mesh);
	//	break;
	//}
}

static void CallExportAnimation(const CAnimSet* Anim)
{
	assert(Anim);
	ExportPsa(Anim);
	//switch (GSettings.Export.SkeletalMeshFormat)
	//{
	//case EExportMeshFormat::psk:
	//default:
	//	ExportPsa(Anim);
	//	break;
	//case EExportMeshFormat::gltf:
	//	appPrintf("ERROR: glTF animation could be exported from mesh viewer only.\n");
	//	break;
	//case EExportMeshFormat::md5:
	//	ExportMd5Anim(Anim);
	//	break;
	//}
}

static void RegisterExporters()
{
	RegisterExporter<USkeletalMesh>([](const USkeletalMesh* Mesh) { CallExportSkeletalMesh(Mesh->ConvertedMesh); });
	RegisterExporter<UMeshAnimation>([](const UMeshAnimation* Anim) { CallExportAnimation(Anim->ConvertedAnim); });
	RegisterExporter<UVertMesh>(Export3D);
	RegisterExporter<UStaticMesh>([](const UStaticMesh* Mesh) { CallExportStaticMesh(Mesh->ConvertedMesh); });
	RegisterExporter<USound>(ExportSound);
#if UNREAL3
	RegisterExporter<USkeletalMesh3>([](const USkeletalMesh3* Mesh) { CallExportSkeletalMesh(Mesh->ConvertedMesh); });
	RegisterExporter<UAnimSet>([](const UAnimSet* Anim) { CallExportAnimation(Anim->ConvertedAnim); });
	RegisterExporter<UStaticMesh3>([](const UStaticMesh3* Mesh) { CallExportStaticMesh(Mesh->ConvertedMesh); });
	RegisterExporter<USoundNodeWave>(ExportSoundNodeWave);
	RegisterExporter<USwfMovie>(ExportGfx);
	RegisterExporter<UFaceFXAnimSet>(ExportFaceFXAnimSet);
	RegisterExporter<UFaceFXAsset>(ExportFaceFXAsset);
#endif // UNREAL3
#if UNREAL4
	RegisterExporter<USkeletalMesh4>([](const USkeletalMesh4* Mesh) { CallExportSkeletalMesh(Mesh->ConvertedMesh); });
	RegisterExporter<UStaticMesh4>([](const UStaticMesh4* Mesh) { CallExportStaticMesh(Mesh->ConvertedMesh); });
	RegisterExporter<USkeleton>([](const USkeleton* Anim) { CallExportAnimation(Anim->ConvertedAnim); });
	RegisterExporter<USoundWave>(ExportSoundWave4);
#endif // UNREAL4
	RegisterExporter<UUnrealMaterial>(ExportMaterial);			// register this after Texture/Texture2D exporters
}


/*-----------------------------------------------------------------------------
	Initialization of class and export systems
-----------------------------------------------------------------------------*/

void InitClassAndExportSystems(int Game)
{
	static bool initialized = false;
	if (initialized) return;
	initialized = true;

	RegisterExporters();
	RegisterClasses(Game);
#if BIOSHOCK
	if (Game == GAME_Bioshock)
	{
		//!! should change this code!
		CTypeInfo::RemapProp("UShader", "Opacity", "Opacity_Bio"); //!!
	}
#endif // BIOSHOCK
}
bool GameFilesCallback_t(const CGameFileInfo* fi, void*pData) {
	TStaticArray<const CGameFileInfo*, 32>* pFiles = (TStaticArray<const CGameFileInfo*, 32> *)pData;
	pFiles->Add(fi);
	//printf("%s\r\n", &fi->GetRelativeName()[0]);
	return true;
}
bool ExportObjects(const TArray<UObject*>* Objects, IProgressCallback* progress)
{
	if (UObject::GObjObjects.Num() == 0) return true;
	appPrintf("Exporting objects ...\n");
	// export object(s), if possible
	UnPackage* notifyPackage = NULL;
	bool hasObjectList = (Objects != NULL) && Objects->Num();
	//?? when 'Objects' passed, probably iterate over that list instead of GObjObjects
	for (UObject* ExpObj : UObject::GObjObjects)
	{
		if (progress && !progress->Tick()) return false;
		bool objectSelected = !hasObjectList || (Objects->FindItem(ExpObj) >= 0);

		if (!objectSelected) continue;

		if (notifyPackage != ExpObj->Package)
		{
			notifyPackage = ExpObj->Package;
			appSetNotifyHeader(*notifyPackage->GetFilename());
		}

		bool done = ExportObject(ExpObj);

		if (!done && hasObjectList)
		{
			// display warning message only when failed to export object, specified from command line
			appPrintf("ERROR: Export object %s: unsupported type %s\n", ExpObj->Name, ExpObj->GetClassName());
		}
	}

	return true;
}
void WriteSourceFile(const CGameFileInfo* FileInfo)
{
	if (!FileInfo) return;
	UnPackage* Package = UnPackage::LoadPackage(FileInfo);
	FArchive* baseLoader = FileInfo->CreateReader(true);
	if (!baseLoader)
	{
		return;
	}
	size_t headerlen = baseLoader->GetFileSize64();
	size_t datalen = Package->GetFileSize64();
	int outLen = datalen+ headerlen;
	byte* pBuffer = new byte[outLen];
	baseLoader->Serialize(pBuffer, (int)headerlen);
	Package->Serialize(&pBuffer[headerlen], (int)datalen);
	FString s = FileInfo->GetRelativeName();
	const char* filename = &s.GetDataArray()[0];
	char writefilename[1024];
	sprintf_s(writefilename, 1024, "e:/Content%s", filename);
	appMakeDirectoryForFile(writefilename);
	FILE* fp = NULL;
	fopen_s(&fp, writefilename, "wb");
	if (fp) {
		fwrite(pBuffer, 1, outLen, fp);
		fclose(fp);
		printf("%s write ok\r\n", writefilename);
	}
	delete pBuffer;
	ReleaseAllObjects();
}
void unpack(const char * projDir)
{
	appSetRootDirectory2(projDir);
	TStaticArray<const CGameFileInfo*, 32> Files;
	TArray<UnPackage*> Packages;
	TArray<UObject*> Objects;
	appFindGameFiles("*.*", Files);
	bool isInit = false;
	for (int j = 0; j < Files.Num(); j++)
	{
		WriteSourceFile(Files[j]);
	}
}
void exportData(const char* projDir) {
	appSetRootDirectory2(projDir);
	TStaticArray<const CGameFileInfo*, 32> Files;
	TArray<UnPackage*> Packages;
	TArray<UObject*> Objects;
	appFindGameFiles("*.*", Files);
	bool isInit = false;
	for (int j = 0; j < Files.Num(); j++)
	{
		UnPackage* Package = UnPackage::LoadPackage(Files[j]);
		if (Package)
		{
			if (!isInit)InitClassAndExportSystems(Package->Game);
			Package->Game = GAME_UE4(20);
			if (!LoadWholePackage(Package, NULL))
			{
				continue;
			}
			// Export
			if (!ExportObjects(NULL, NULL))
			{
				continue;
			}
			ReleaseAllObjects();
		}
	}
}
int main()
{
    //appSetRootDirectory2("E:/Spacebourne/ASPv3/Content/");
	//appSetRootDirectory2("E:/witchtastic/Witchtastic/Content/");
	//unpack("E:/witchtastic/Witchtastic/Content/");
	exportData("E:/Content/");
    std::cout << "Hello World!\n";
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
